git hooks文档:https://git-scm.com/docs/githooks
husky: https://github.com/typicode/husky
lint-staged: https://github.com/okonet/lint-staged
2020.9.18 星期五 :
做一些hooks相关的工作。比如:commit前 检查/格式化代码;规范提交信息/提交信息模版;
CI/CD
git hooks
在项目根目录的 .git/hooks 下面配置,配置文件的名称是固定的,使用shell语法编写。
Git Hooks 介绍
.git/hooks文件下,保存了一些 shell 脚本,然后在对应的钩子中执行这些脚本就行了。
一个还没有配置 Git Hooks 的仓库,默认会有很多.sample结尾的文件,这些都是示例文件
这个脚本默认是不生效的,如果要生效,把文件名后缀去掉就可以了
not set as executable
问题: hint: The ‘.git/hooks/pre-commit’ hook was ignored because it’s not set as executable.
The sample files from a git init are all executable; if it’s copied or renamed to a non-sample file, it will retain the original file’s x flag.
New files will be created with current defaults. In your case, view those defaults with umask:
默认情况下,u+x除非明确设置为新文件,否则不会。$ umask #0022
解决chmod +x
自动化脚本
就像附加答案一样,这里是函数,你可以用来初始化一个git存储库,它自动生成钩子可执行文件;
您应该将它放入.bashrc或在启动终端时从源文件中找到它。故事在下面:)1
2
3
4
5
6
7
8
9 ginit () {
git init
gitpath=`git rev-parse --show-superproject-working-tree --show-toplevel | head -1`
chmod u+x "$gitpath"/.git/hooks/*
for submodule in "$gitpath"/.git/modules/*; do
chmod u+x "$submodule"/hooks/*
done
}
}
我和你一样生气。我不想记住每次初始化存储库时都必须创建所有挂钩可执行文件。
另外,当你使用子模块时,它们的钩子不在.git/hooks,
但是在.git/modules/NameOfSubmodule/hooks,并且这些钩子也应该是可执行的。
示例shell
模版目录
如果我们所有项目都需要一个通用的钩子,那么我们需要在所有的项目中都放置钩子文件。挨个复制显然不是一个可行的方案。
在 git init 或者 git clone时,如果指定有模板目录,会使用拷贝模板目录下的文件到 .git/ 目录下。1
2 git init --template "path-to-template-dir"
git clone --template "path-to-template-dir"
全局配置
1.创建/usr/local/.git_template/.git_template目录
2.将第一步中创建的commit_msg文件拷贝至上方目录
3.使用该命令将git全局配置模版重定向到第一步中创建到目录git config --global init.templatedit /usr/local/.git_template/.git_template
4.如上三步完成后即可在创建新的使用git管理的项目的时候自动将全局模版拷贝至项目根目录/.git/目录下,
如果完成如上三步后需要对已经存在对git项目使用该模版,可移动至目标项目根目录并执行git init即可
1 | 定义模板目录,模板目录下的钩子目录 |
why husky
.git文件夹不会提交到git,这就导致一个问题,我们在本地配置好 Git Hook 后,怎么分享给其他小伙伴儿呢?copy 吗?
那未免太 low 了,都用 Git 了,还 copy,也太不优雅了。这时候,就轮到
scripts
package.json中也可以设置1
2
3
4
5
6
7"scripts": {
"dev": "wxxxx build --dev",
"preinstall": "node build/bin/reset-hooks",
"preparecommitmsg": "node build/bin/custom-lint -e $GIT_PARAMS",
"postcommit": "node build/bin/post-commit",
"prepush": "node build/bin/pre-push -e $GIT_PARAMS",
}
husky
husky: https://typicode.github.io/husky/#/?id=add-a-hook
github: https://github.com/typicode/husky
Husky 是一个让配置 Git 钩子变得更简单的工具(题外话:Husky 是哈士奇的意思,我猜可能是作者养了条二哈)
下面这些流行的项目都在使用 Husky,可见它确实是一个非常好用的工具:webpack, babel, create-react-app
1 | npm install husky --save-dev |
PS: 原理分析。执行npx husky install
的时候,把git hooks 的路径指向.husky。
package.json1
2
3
4
5
6
7
8
9"scripts": {
"prepare": "npx husky install",
"lint": "eslint src"
},
"husky": {
"hooks": {
"pre-commit": "npm run lint"
}
},
原理分析
通过查看源码可以看到,在安装 husky 的时候,husky会根据 package.json里的配置,
在.git/hooks 目录生成所有的 hook 脚本(如果你已经自定义了一个hook脚本,husky不会覆盖它)
具体步骤
1、husky 使用了自定义的安装过程:node lib/installer/bin install
(在node_modules/husky/package.json里)。
执行的时会在项目的.git/hooks 目录生成所有 hook 的脚本
2、每个hook脚本都是一样的1
2
3
4
5
# husky
# v1.0.0-rc.1 darwin
export HUSKY_GIT_PARAMS="$*"
node_modules/run-node/run-node ./node_modules/husky/lib/runner/bin `basename "$0"`
关键的部分是 bashname "$0"
,这样可以拿到当前的 hook名,如pre-commit、pre-push 。
3、最后根据package.json 的配置,执行我们定义相对应的hook脚本。
lint-stage
lint-staged: https://github.com/okonet/lint-staged
用于实现每次提交只检查本次提交所修改的文件。
yorkie
yorkie fork 自 husky 并且与后者不兼容。
Git hooks made easy
This is a fork of husky with a few changes:
vue-cli-serve
在安装之后,@vue/cli-service 也会安装 yorkie ,它会让你在 package.json 的 gitHooks 字段中方便地指定 Git hook:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 {
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"lintcss": "stylelint src/**/*.{html,vue,css,less,scss} --fix",
"preparecommitmsg": "node build/bin/custom-lint -e $GIT_PARAMS",
"postcommit": "node build/bin/post-commit",
"analyz": "ANALYZ=true vue-cli-service build",
"project": "node ./scripts/build/projectInit.js"
},
"gitHooks": {
"pre-commit": "lint-staged",
"commit-msg": "node scripts/git/verify-commit-msg.js"
},
"lint-staged": {
"src/**/*.{js,jsx,vue,ts,tsx}": [
"vue-cli-service lint",
"git add"
],
"src/**/*.{vue,less,scss}": [
"npm run lintcss",
"git add"
]
},
"eslintIgnore": [
"/scripts/git/verify-commit-msg.js",
"/scripts/build/projectInit.js",
"/dist",
"/build",
"/vue.config.js",
"/tsconfig.json"
],
"stylelint": {
"extends": "@xes/stylelint-config-xes",
"rules": {
"value-list-comma-newline-after": "always-multi-line"
}
},
"stylelintIgnore": [
"/src/layout/index.html",
"src/assets/less/utils"
],
}
commitlint
commitlint: https://commitlint.js.org/#/guides-local-setup1
2
3# Add hook
npx husky add .husky/pre-commit "npm test"
npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
1 | npm install --save-dev husky |
1 | "husky": { |
————其他————-
husky + lint-staged + commitlint
- 安装 husky,lint-staged,@commitlint/cli,@commitlint/config-conventional 依赖
lint-staged: 用于实现每次提交只检查本次提交所修改的文件。 创建 .huskyrc
1
2
3
4
5
6{
"hooks": {
"pre-commit": "lint-staged",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}创建 .lintstagedrc
1
2
3
4
5
6
7
8
9
10
11{
"src/**/*.js": "eslint"
}
// 设置 fix 可以自动修复错误:
{
"src/**/*.js": ["eslint --fix", "git add"]
},
// 或者使用下面的配置,自动格式化代码(谨慎使用)
{
"src/**/*.js": ["prettier --write", "git add"]
}创建 commitlint.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat', // 新功能(feature)
'fix', // 修补bug
'docs', // 文档(documentation)
'style', // 格式(不影响代码运行的变动)
'refactor', // 重构(即不是新增功能,也不是修改bug的代码变动)
'test', // 增加测试
'revert', // 回滚
'config', // 构建过程或辅助工具的变动
'chore', // 其他改动
],
],
'type-empty': [2, 'never'], // 提交不符合规范时,也可以提交,但是会有警告
'subject-empty': [2, 'never'], // 提交不符合规范时,也可以提交,但是会有警告
'subject-full-stop': [0, 'never'],
'subject-case': [0, 'never'],
}
}
eslint + prettier + husky
.eslintrc.js1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint'
},
extends: [
'plugin:vue/essential',
'@xes/eslint-config-xes',
'plugin:prettier/recommended'
],
plugins: ['html'],
rules: {
'arrow-parens': 0,
'generator-star-spacing': 0,
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
camelcase: 0
}
}
package.json1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16{
"scripts": {
"lint": "prettier --ignore-path --write './src/**/*.{js,json,css,vue}' && eslint --ext .js,.vue src"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.{js,json,css,vue}": [
"npm run lint",
"git add"
]
}
}